/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package raytracer;

/**
 *
 * @author ferco
 * Trida koule, vykresli kouli na danem umisteni a daneho polomeru
 */
public class Sphere implements Primitive{

    //deklarace promennych
    private Vector3D location;
    private double radius;
    private double squareRadius;
    private Material material;

    /**
     *Konsturktor, vytvori kouli na dane lokaci, daneho polomeru a materialu
     *@param location   umisteni koule
     *@param radius     polomer koule
     *@param material   material ze ktereho je vyrobena
     */
    public Sphere(Vector3D location, double radius, Material material){

        this.location = location;
        this.radius = radius;
        this.squareRadius = radius * radius;
        this.material = material;
    }

    /**
     * Metoda pro zjisteni jestli dany paprsek trefi kouli
     * @param ray
     * @return intersection pokud existuje, null pokud paprsek minul kouli
     */
    public Intersection findIntersection(Ray ray){

        //vypocet jednotlivych parametru kvadraticke rovnice
        double a = 1;//ray.getDirection().dot(ray.getDirection());
        double b = 2*ray.getDirection().dot(ray.getOrigin().minusVector(this.location));
        double c = ray.getOrigin().minusVector(this.location).dot(ray.getOrigin().minusVector(this.location)) - this.squareRadius;

        //vypocet diskriminantu
        double t = b*b - 4*c;

        //pokud je diskriminant zaporny, rovnice nema reseni a paprsek minul kouli
        if (t < 0){
            return null;
        }

        //pokud je diskriminant 0 existuje prave jedno reseni a tudiz i jen jeden prusecik
        if (t == 0){
            
            //vypocitej prusecik
            double s = (- b) / 2 ;
            if (Math.abs(s)< 0.00000001){
                Vector3D point = ray.getOrigin().plusVector(ray.getDirection().multiplyBy(s));
                Vector3D movedPoint = point.plusVector(this.calculateNormal(point).multiplyBy(0.00001));
                return new Intersection(movedPoint, s, this);
            }
            return new Intersection(ray.getOrigin().plusVector(ray.getDirection().multiplyBy(s)), s, this);
        }

        //pokud je diskriminant vetsi jak 0 mame dve reseni, staci nam vsak pocitat to reseni
        //ktere je blize pocatku paprsku to znamena to s minus
        if (t > 0){
            //vypocitej blizsi prusecik
            double s = (- b - Math.sqrt(t)) / 2 ;
            //prusecik lezi za kamerou
            if (Math.abs(s)< 0.000001){
                Vector3D point = ray.getOrigin().plusVector(ray.getDirection().multiplyBy(s));
                Vector3D movedPoint = point.plusVector(this.calculateNormal(point).multiplyBy(0.00001));
                return new Intersection(movedPoint, s, this);
             
            }
            if (s <=0){
            s = (- b + Math.sqrt(t)) / 2 ;
            }

            if (s<0){
                return null;
            }
            //vytvor nove intersection
            return new Intersection(ray.getOrigin().plusVector(ray.getDirection().multiplyBy(s)), s, this);
            //return null;
        }

        return null;
    }

    public Intersection findGeometricIntersection(Ray ray){

        Vector3D originToCenter = this.getLocation().minusVector(ray.getOrigin());     

        double l2oc = originToCenter.dot(originToCenter);

        if(l2oc<this.squareRadius){
            return null;
        }

        double tca = originToCenter.dot(ray.getDirection());

        if (tca<0){
            return null;
        }

        double t2hc = this.squareRadius - l2oc + tca*tca;

        if (t2hc < 0){
            return null;
        }

        double t = tca - Math.sqrt(t2hc);
        return new Intersection(ray.getOrigin().plusVector(ray.getDirection().multiplyBy(t)), t, this);

    }

    /**
     * metoda pro vypocet normaly z daneho bodu na kouli
     * @param point
     * @return normal
     */
     public Vector3D calculateNormal(Vector3D point){

        //vypocet jednotlivych slozek normaly
        double a =(point.getX() - this.getLocation().getX()) / this.getRadius();
        double b =(point.getY() - this.getLocation().getY()) / this.getRadius();
        double c =(point.getZ() - this.getLocation().getZ()) / this.getRadius();

        //vrat vektor predstavujici normalu
        return new Vector3D(a,b,c);
    }

    public double getRadius() {
        return radius;
    }

    public Vector3D getLocation() {
        return location;
    }

    public Material getMaterial() {
        return material;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    public void setLocation(Vector3D location) {
        this.location = location;
    }

    public void setMaterial(Material material) {
        this.material = material;
    }



}
